#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "globals.h"
#include "system_timer.c"
#include "scheduler.c"
#include "system_call_library.h"
#include "cpu.c"
#include "io_device.c"
#include <termios.h>
#include <term.h>
#include <curses.h>
#include <unistd.h>
#include "semaphore.c"

void init_keyboard();
void close_keyboard();
int kbhit();
int readch();
int get_key_pressed();


/*##################################################################################################
 # Variables
 ##################################################################################################*/

int simulator_countdown;



/**
This function creates a process based on the parameter.
For eg if 1 is passed, cp1 is created.
**/
void create_process(int process)
{

switch(process){

case 1:
{
//Calculating process
ProcessStr *calc_process = malloc(sizeof(ProcessStr)); //Calculating process
strcpy(calc_process->name, "cp1");
calc_process->no_steps = rand_num();
calc_process->no_requests = 0;//right now this process does not make any requests
printf("Process cp1 created. This process , does some basic computing.\n");
add_process_to_readyqeueue(calc_process);
break;
}
case 2:
{
//Process which makes video IO request
ProcessStr *video_process = malloc(sizeof(ProcessStr));
strcpy(video_process->name, "cp2");
video_process->no_steps = rand_num();
video_process->no_requests = 1;
video_process->requests = malloc(sizeof(int));
video_process->req_type =  malloc(sizeof(char));
video_process->requests[0] = (video_process->no_steps)/2;
video_process->req_type[0] = 'v'; //request is for video io device.
printf("Process cp2 created which will make an video io request.\n");
add_process_to_readyqeueue(video_process);
break;
}
case 3:
{
//Process which makes disk IO request
ProcessStr *disk_process = malloc(sizeof(ProcessStr));
strcpy(disk_process->name, "cp3");
disk_process->no_steps = rand_num();
disk_process->no_requests = 1;
disk_process->requests = malloc(sizeof(int));
disk_process->req_type =  malloc(sizeof(char));
disk_process->requests[0] = (disk_process->no_steps)/2;
disk_process->req_type[0] = 'd'; //request is for disk io device.
printf("Process cp3 created which will make an disk io request.\n");
add_process_to_readyqeueue(disk_process);
break;
}
case 4:
{
//Producer process which does a memory write call
ProcessStr *producer_process = malloc(sizeof(ProcessStr));
strcpy(producer_process->name, "cp4");
producer_process->no_steps = rand_num();
producer_process->no_requests = 1;
producer_process->requests = malloc(sizeof(int));
producer_process->req_type =  malloc(sizeof(char));
producer_process->requests[0] = (producer_process->no_steps)/2;
producer_process->req_type[0] = 'w';//mem_write call 
printf("Process cp4 created which will make a memory write call.\n");
add_process_to_readyqeueue(producer_process);
break;
}
case 5:
{
//Consumer process which makes memory read call
ProcessStr *consumer_process = malloc(sizeof(ProcessStr));
strcpy(consumer_process->name, "cp5");
consumer_process->no_steps = rand_num();
consumer_process->no_requests = 1;
consumer_process->requests = malloc(sizeof(int));
consumer_process->req_type =  malloc(sizeof(char));
consumer_process->requests[0] = (consumer_process->no_steps)/2;
consumer_process->req_type[0] = 'r'; //mem_read call 
printf("Process cp5 created which will make a memory read call.\n");
add_process_to_readyqeueue(consumer_process);
break;
}

default:
{
	printf("Wrong process number.\n");
}

}


}




/**
The parameters passed to the program will be 
the number of the processes (a pre-assigned menu of processes - see below)
 a user interface process (e.g. one waiting for keyboard input)
 a calculating process (possible background job)
 two processes that require inter-process communications, such as a producer-consumer pair
the order to initialize and start them
the number of total time ticks to run the simulation.
**/

int main(int argc, char** args) {

if( argc < 3 )
  {
    printf("Wrong syntax. Syntax is <no of processes> <no of ticks the simulator will run> optional <order of processes; 1 for cp1, 2 for cp2 ,...>\n");
    exit(0);
  }
else if( argc > 6 )
  {
    printf("Too many arguments supplied.\n");
    exit(0);
  }
if(atoi(args[1]) == 0)
{
    printf("You selected 0 processes. Goodbye.\n");
    exit(0);
}
else if(atoi(args[2]) == 0)
{
    printf("You selected 0 ticks for the simulator to run. Goodbye.\n");
}

//start the keyboard scanning
init_keyboard();

//Funtionality starts here.
simulator_countdown = atoi(args[2]); //no to ticks the scheduler would run

//Setup Scheduler
create_ready_queue();

//Setup IO Devices
set_up_disk();
set_up_video();
scheduler_start(); //gets the next process running
setup_semaphores();

if( argc > 3)
{
	int no_of_processes = atoi(args[1]);
	int i = 3;
	while( no_of_processes != 0)
	{
		create_process(atoi(args[i]));
		no_of_processes --;
		i++;
	}
}
else
{
  int no_of_processes = atoi(args[1]);
  int i = 0;
  while( no_of_processes != i)
  {
	create_process(i + 1);
	i++;
   }
}


while(simulator_countdown != 0)
{
	if(ready_queue->head->next->state == 0)
	{
	   printf("Process %s is currently running\n", ready_queue->head->next->pid->name);
	}
	cpu();
	simulator_countdown--;

}
if(simulator_countdown == 0)
{
	puts("The simulation is done. Exiting now.\n");
}
	//end the keyboard scanning
	close_keyboard();
	return 0;
}

static struct termios initial_settings, new_settings;
static int peek_character = -1;

void init_keyboard()
{
    tcgetattr(0,&initial_settings);
    new_settings = initial_settings;
    new_settings.c_lflag &= ~ICANON;
    new_settings.c_lflag &= ~ECHO;
    new_settings.c_lflag &= ~ISIG;
    new_settings.c_cc[VMIN] = 1;
    new_settings.c_cc[VTIME] = 0;
    tcsetattr(0, TCSANOW, &new_settings);
}

void close_keyboard()
{
    tcsetattr(0, TCSANOW, &initial_settings);
}

int kbhit()
{
    char ch;
    int nread;

    if(peek_character != -1)
        return 1;
    new_settings.c_cc[VMIN]=0;
    tcsetattr(0, TCSANOW, &new_settings);
    nread = read(0,&ch,1);
    new_settings.c_cc[VMIN]=1;
    tcsetattr(0, TCSANOW, &new_settings);

    if(nread == 1) {
        peek_character = ch;
        return 1;
    }
    return 0;
}

int readch()
{
    char ch;

    if(peek_character != -1) {
        ch = peek_character;
        peek_character = -1;
        return ch;
    }
    read(0,&ch,1);
    
    //clear the buffer, one read doesn't seem to do it.
    while (kbhit()) {
    	readch();
    }
    
    return ch;
}


int get_key_pressed() {
	int ch = readch();
	while (kbhit()) {
		readch();
	}
	return ch;
}



